using System;
using System.IO;
using System.Text;
using System.Collections.Generic;
using Net.Unity;
using Net.Helper;
using UnityEngine;
using Newtonsoft_X.Json;
using Cysharp.Threading.Tasks;
using UnityEngine.SceneManagement;
using Object = UnityEngine.Object;

namespace GameCore
{
    public class AssetPackage
    {
        public string Name { get; set; }
        public string Version { get; set; }
        public bool Encrypt { get; set; }
        public List<AssetInfo> AssetInfos { get; set; } = new();
        public List<AssetBundleInfo> AssetBundleInfos { get; set; } = new();
        public Dictionary<string, string[]> Dependencies { get; set; } = new();
        [JsonIgnore] public Dictionary<string, AssetInfo> AssetInfoDict { get; set; } = new();
        [JsonIgnore] public Dictionary<string, AssetBundleInfo> AssetBundleDict { get; set; } = new();
        internal AssetPackageManager PackageManager { get; set; }
        [JsonIgnore] public AssetPackageInfo PackageInfo { get; set; }

        internal void Init()
        {
            AssetInfoDict.Clear();
            AssetBundleDict.Clear();
            foreach (var assetBundleInfo in AssetBundleInfos)
            {
                assetBundleInfo.assetPackage = this;
                AssetBundleDict.Add(assetBundleInfo.Name, assetBundleInfo);
            }
            foreach (var assetInfo in AssetInfos)
            {
                assetInfo.assetPackage = this;
                AssetInfoDict.Add(assetInfo.AssetPath, assetInfo);
            }
            Debug.Log($"包{Name}初始化完成，资源数量:{AssetInfoDict.Count}");
        }

        public virtual AssetBundle LoadAssetBundle(string assetBundlePath) => LoadAssetBundleAsync(assetBundlePath, false).GetAwaiter().GetResult();

        public virtual async UniTask<AssetBundle> LoadAssetBundleAsync(string assetBundlePath, bool isAsync = true)
        {
            if (isAsync)
            {
                if (!PackageManager.assetBundleAsyncDict.TryGetValue(assetBundlePath, out var assetBundleLoadAsync))
                {
                    PackageManager.assetBundleAsyncDict.Add(assetBundlePath, assetBundleLoadAsync = new());
                    var bytes = await LoadAssetFileAsync(assetBundlePath, Encrypt, isAsync);
                    if (bytes == null)
                    {
                        Debug.LogError($"加载AB文件失败:{assetBundlePath}");
                        return null;
                    }
                    assetBundleLoadAsync.assetBundleCreate = AssetBundle.LoadFromMemoryAsync(bytes);
                }
                while (!assetBundleLoadAsync.IsDone)
                    await UniTask.Yield();
                assetBundleLoadAsync.ReleaseTime = Time.realtimeSinceStartup + 5f;
                return assetBundleLoadAsync.AssetBundle;
            }
            else
            {
                var bytes = await LoadAssetFileAsync(assetBundlePath, Encrypt, isAsync);
                if (bytes == null)
                    return null;
                return AssetBundle.LoadFromMemory(bytes);
            }
        }

        public static async UniTask<byte[]> LoadAssetFileAsync(string assetPath, bool isEncrypt = false, bool isAsync = true)
        {
        J:
            var bytes = UnityWebRequestEx.Get(assetPath, request =>
            {
                var bytes = request.downloadHandler.data;
                if (isEncrypt)
                    EncryptHelper.ToDecrypt(Global.Config.Password, bytes);
                return bytes;
            }, null, Global.Config.Timeout);
            if (bytes == null && Global.Config.Mode == AssetBundleMode.ServerMode)
            {
                var count = Global.Config.ApplicationPath.Length;
                var url = Global.Config.Url + assetPath.Remove(0, count);
                if (isAsync)
                {
                    var name = Path.GetFileNameWithoutExtension(assetPath);
                    Global.UI.Wait.ShowUI($"资源加载中...", 0f);
                    var isComplete = await DownloadManager.DownloadFile(url, assetPath, name, (progressText, progress) =>
                    {
                        Global.UI.Wait.ShowUI($"资源加载中...", progress);
                    });
                    Global.UI.Wait.HideUI();
                    if (isComplete)
                        goto J;
                }
                else
                {
                    Global.Logger.Log($"同步下载资源:{url}");
                    var isComplete = UnityWebRequestEx.Get(url, new FileDHParameter(assetPath), request => true, null, Global.Config.Timeout);
                    if (isComplete)
                        goto J;
                }
            }
            return bytes;
        }

        /// <summary>
        /// 加载资源，遍历所有资源进行查找尝试加载资源， 如果成功则直接返回
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="assetPath"></param>
        /// <returns></returns>
        public virtual T LoadAsset<T>(string assetPath) where T : Object => LoadAssetAsync<T>(assetPath, false).GetAwaiter().GetResult();

        public virtual async UniTask<T> LoadAssetAsync<T>(string assetPath, bool isAsync = true) where T : Object
        {
            assetPath = GetAssetPath(assetPath);
            var assetBundle = await GetAssetBundleAsync(assetPath, isAsync);
            if (assetBundle == null)
            {
                Debug.LogError($"空AB文件:{assetPath}");
                return default;
            }
            if (typeof(T) == typeof(Sprite))
                return assetBundle.LoadAsset<T>(assetPath);
            //这里只能获取Object类型，如果直接获取T会获取失败，比如你获取GameObject里面的Animator组件，你直接LoadAsset<Animator>("xx")会失败!
            Object assetObject;
            if (!isAsync)
                assetObject = assetBundle.LoadAsset(assetPath, typeof(Object));
            else
                assetObject = await assetBundle.LoadAssetAsync(assetPath, typeof(Object));
            return GetAsset<T>(assetObject);
        }

        public virtual T[] LoadAssetWithSubAssets<T>(string assetPath) where T : Object
            => LoadAssetWithSubAssetsAsync<T>(assetPath, false).GetAwaiter().GetResult();

        public virtual async UniTask<T[]> LoadAssetWithSubAssetsAsync<T>(string assetPath, bool isAsync = true) where T : Object
        {
            assetPath = GetAssetPath(assetPath);
            Object[] assetObjects;
            var assetBundle = await GetAssetBundleAsync(assetPath, isAsync);
            if (assetBundle == null)
                return default;
            if (isAsync)
            {
                var assetBundleRequest = assetBundle.LoadAssetWithSubAssetsAsync(assetPath);
                await assetBundleRequest;
                assetObjects = assetBundleRequest.allAssets;
            }
            else
            {
                assetObjects = assetBundle.LoadAssetWithSubAssets(assetPath);
            }
            return ConvertObjects<T>(assetObjects);
        }

        public static T[] ConvertObjects<T>(Object[] assetObjects) where T : Object
        {
            var subAssets = new List<T>(assetObjects.Length);
            for (int i = 0; i < assetObjects.Length; i++)
                if (assetObjects[i] is T assetObject) //这里获取Sprite, 这里的第一个元素会是Texture2D
                    subAssets.Add(assetObject);
            return subAssets.ToArray();
        }

        protected virtual AssetBundle GetAssetBundle(string assetPath) => GetAssetBundleAsync(assetPath, false).GetAwaiter().GetResult();

        public bool TryGetAssetInfo(string assetPath, out AssetInfo assetInfo)
        {
            return AssetInfoDict.TryGetValue(assetPath, out assetInfo);
        }

        public bool TryGetAssetBundle(string assetBundleName, out AssetBundle assetBundle)
        {
            return PackageManager.assetBundles.TryGetValue(assetBundleName, out assetBundle);
        }

        public bool AddAssetBundle(string assetBundleName, AssetBundle assetBundle)
        {
            return PackageManager.assetBundles.TryAdd(assetBundleName, assetBundle);
        }

        public virtual async UniTask<AssetBundle> GetAssetBundleAsync(string assetPath, bool isAsync = true)
        {
            if (!TryGetAssetInfo(assetPath, out var assetInfo))
            {
                Debug.LogError($"空资源字典:{assetPath}");
                return null;
            }
            if (!TryGetAssetBundle(assetInfo.AssetBundleName, out var assetBundle))
            {
                assetBundle = await LoadAssetBundleAsync(assetInfo.AssetBundlePath, isAsync);
                if (assetBundle != null)
                {
                    var result = AddAssetBundle(assetInfo.AssetBundleName, assetBundle);
                    if (result)
                        await DirectDependenciesAsync(assetInfo.AssetBundleName, isAsync);
                }
            }
            return assetBundle;
        }

        public virtual bool LoadAssetScene(string assetPath, LoadSceneMode mode = LoadSceneMode.Single)
        {
            assetPath = GetAssetPath(assetPath);
            if ((byte)Global.I.Mode <= 1)
            {
                assetPath = Path.GetFileNameWithoutExtension(assetPath);
                goto J;
            }
            var assetBundle = GetAssetBundle(assetPath);
            if (assetBundle == null)
                return false;
            J: UnityEngine.SceneManagement.SceneManager.LoadScene(assetPath, mode);
            return true;
        }

        public virtual async UniTask LoadAssetSceneAsync(string assetPath, Action onLoadComplete = null, LoadSceneMode mode = LoadSceneMode.Single)
        {
            await LoadAssetSceneAsync(assetPath, mode, (progress) => Global.UI.Loading.ShowUI("加载场景中..." + (progress * 100f).ToString("f0") + "%", progress), () =>
            {
                onLoadComplete?.Invoke();
                Global.UI.Loading.HideUI();
            });
        }

        public virtual async UniTask LoadAssetSceneAsync(string assetPath, LoadSceneMode mode = LoadSceneMode.Single, Action<float> progress = null, Action onLoadComplete = null)
        {
            assetPath = GetAssetPath(assetPath);
            var assetBundle = await GetAssetBundleAsync(assetPath);
            if (assetBundle == null)
                return;
            var asyncOper = UnityEngine.SceneManagement.SceneManager.LoadSceneAsync(assetPath, mode);
            asyncOper.allowSceneActivation = false;
            while (asyncOper.progress < 0.9f)
            {
                progress?.Invoke(asyncOper.progress);
                await UniTask.Yield();
            }
            progress?.Invoke(1f);
            await UniTask.Delay(1000);
            asyncOper.allowSceneActivation = true;
            onLoadComplete?.Invoke();
        }

        protected virtual void DirectDependencies(string assetBundleName) => DirectDependenciesAsync(assetBundleName, false).GetAwaiter().GetResult();

        protected virtual async UniTask DirectDependenciesAsync(string assetBundleName, bool isAsync = true)
        {
            var dependencies = GetDirectDependencies(assetBundleName);
            foreach (var dependencie in dependencies)
            {
                if (ContainsAssetBundle(dependencie))
                    continue;
                var assetBundleInfo = Global.Resources.PackageManager.GetAssetBundleInfo(dependencie);
                var assetBundle = await LoadAssetBundleAsync(assetBundleInfo.AssetBundlePath, isAsync);
                var result = AddAssetBundle(dependencie, assetBundle);
                if (result)
                    await DirectDependenciesAsync(dependencie, isAsync);
            }
        }

        public string[] GetDirectDependencies(string assetBundleName)
        {
            if (Dependencies.TryGetValue(assetBundleName, out var directDependencies))
                return directDependencies;
            return new string[0];
        }

        public bool ContainsAssetBundle(string assetBundleName)
        {
            return PackageManager.assetBundles.ContainsKey(assetBundleName);
        }

        public T Instantiate<T>(string assetPath, Transform parent = null) where T : Object
        {
            return Instantiate<T>(assetPath, Vector3.zero, Quaternion.identity, parent);
        }

        public async UniTask<T> InstantiateAsync<T>(string assetPath, Transform parent = null, bool isAsync = true) where T : Object
        {
            return await InstantiateAsync<T>(assetPath, Vector3.zero, Quaternion.identity, parent, isAsync);
        }

        public T Instantiate<T>(string assetPath, Vector3 position, Quaternion rotation, Transform parent = null) where T : Object
        {
            return InstantiateAsync<T>(assetPath, position, rotation, parent, false).GetAwaiter().GetResult();
        }

        public async UniTask<T> InstantiateAsync<T>(string assetPath, Vector3 position, Quaternion rotation, Transform parent = null, bool isAsync = true) where T : Object
        {
            var assetObj = await LoadAssetAsync<T>(assetPath, isAsync);
            if (assetObj == null)
            {
                Global.Logger.LogError($"资源加载失败:{assetPath}");
                return null;
            }
            return Object.Instantiate(assetObj, position, rotation, parent);
        }

        /// <summary>
        /// 获取资源名，仅使用文件名时，不包含后缀；否则需要带后缀后的完整路径
        /// </summary>
        /// <param name="assetPath"></param>
        /// <returns></returns>
        public static string GetAssetPath(string assetPath)
        {
            if (Global.I.Addressables)//仅使用文件名
                assetPath = Path.GetFileNameWithoutExtension(assetPath);
            return assetPath;
        }

        public static T GetAsset<T>(Object assetObject) where T : Object
        {
            if (assetObject is GameObject gameObject)
            {
                if (typeof(T) == typeof(GameObject) || typeof(T) == typeof(Object)) //如果获取的是游戏物体或者基类则直接返回
                    return assetObject as T;
                if (gameObject.TryGetComponent(typeof(T), out var component))
                    return component as T;
            }
            return assetObject as T;
        }

        public bool ContainsAssetInfo(string assetName)
        {
            return AssetInfoDict.ContainsKey(assetName);
        }

        public int GetAssetInfoCount()
        {
            return AssetInfos.Count;
        }

        public void AddAssetInfo(string assetName, string assetBundleName, string md5)
        {
            lock (this)
            {
                if (!AssetBundleDict.ContainsKey(assetBundleName))
                {
                    var assetBundleInfo = new AssetBundleInfo(assetBundleName, string.Empty, 0)
                    {
                        assetPackage = this
                    };
                    AssetBundleDict.Add(assetBundleName, assetBundleInfo);
                    AssetBundleInfos.Add(assetBundleInfo);
                }

                if (!AssetInfoDict.ContainsKey(assetName))
                {
                    var assetInfo = new AssetInfo
                    {
                        AssetPath = assetName,
                        AssetBundleName = assetBundleName,
                        MD5 = md5,
                        assetPackage = this
                    };
                    AssetInfos.Add(assetInfo);
                    AssetInfoDict.Add(assetName, assetInfo);
                }
            }
        }

        public bool CheckMD5(AssetInfo assetInfo)
        {
            if (AssetInfoDict.TryGetValue(assetInfo.AssetPath, out var assetInfo1))
                return assetInfo.MD5 == assetInfo1.MD5;
            return false;
        }

        public override string ToString()
        {
            return Name;
        }
    }
}
